home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 April: Mac OS SDK / Dev.CD Apr 98 SDK1.toast / Development Kits (Disc 1) / Macintosh Drag and Drop / Demo Applications / Dragster / DragText Sources / drag.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-03  |  22.2 KB  |  951 lines  |  [TEXT/KAHL]

  1. /*
  2.  *
  3.  *        drag.c
  4.  *
  5.  *        Text dragging routines. The code in this file implements the text
  6.  *        dragging features of this program. This is the single most important
  7.  *        file in the DragText program. The last function in this file,
  8.  *        DragText is called when a mouseDown is detected in the hilite region
  9.  *        of the current TextEdit field. To follow the text dragging code,
  10.  *        start with the DragText function.
  11.  *
  12.  *
  13.  *        Author:        Rob Johnston
  14.  *        Date:        Friday, September 10, 1993
  15.  *
  16.  *        12/30/94    JML        Code now compiles with Universal interfaces.
  17.  *        12/30/94    JML        Replaced DropLocationIsFinderTrash with new version of function.
  18.  
  19.  *
  20.  *        Copyright © 1992-93 Apple Computer, Inc.
  21.  *
  22.  */
  23.  
  24. #include <Errors.h>
  25. #include <QDOffscreen.h>
  26. #include <AppleEvents.h>
  27. #include <Files.h>
  28. #include <Folders.h>
  29. #include <Drag.h>
  30. #include "globals.h"
  31. #include "prototypes.h"
  32. #include "Offscreen.h"
  33. #include "DTResources.h"
  34.  
  35.  
  36. //
  37. //    Can use custom drawing procedure.
  38. //
  39.  
  40. //    #define USE_CUSTOM_DRAWING
  41.  
  42.  
  43.     static long            caretTime;
  44.     static short        caretOffset, caretShow, lastOffset, insertPosition, canAcceptItems;
  45.     static short        cursorInContent;
  46.  
  47.  
  48. /*
  49.  *    The following #define statement provides access to TextEdit's caretTime.
  50.  */
  51.  
  52. #define    gCaretTime        ((short) *((long *) 0x02F4))
  53.  
  54.  
  55. /*
  56.  *    MyDrawingProc
  57.  *
  58.  *    Simple drawing proc for dragging text. This drawing procedure is very similar to
  59.  *    the Drag Manager's built in drawing proc, except that it uses a solid black pattern
  60.  *    to draw the region instead of using the dithered gray pattern.
  61.  */
  62.  
  63. #ifdef USE_CUSTOM_DRAWING
  64.  
  65. pascal OSErr MyDrawingProc(DragRegionMessage message,
  66.                            RgnHandle showRgn, Point showOrigin,
  67.                            RgnHandle hideRgn, Point hideOrigin,
  68.                            void *dragDrawingRefCon, DragReference theDragRef)
  69.  
  70. {    OSErr            result = paramErr;
  71.     RgnHandle        tempRgn;
  72.  
  73.     switch(message) {
  74.  
  75.         case dragRegionBegin:
  76.  
  77.             //
  78.             //    No initialization necessary for our drawing proc. Make sure noErr
  79.             //    is returned, otherwise the Drag Manager will revert back to it's
  80.             //    built in drawing proc.
  81.             //
  82.  
  83.             result = noErr;
  84.             break;
  85.  
  86.         case dragRegionDraw:
  87.  
  88.             //
  89.             //    Find the difference between the region needed to be shown and the
  90.             //    region needed to be hidden. Inverting the difference removes the pixels
  91.             //    that must be hidden and shows the pixels that must be shown in one step.
  92.             //
  93.  
  94.             XorRgn(showRgn, hideRgn, tempRgn = NewRgn());
  95.             InvertRgn(tempRgn);
  96.             DisposeRgn(tempRgn);
  97.             result = noErr;
  98.             break;
  99.  
  100.         case dragRegionHide:
  101.  
  102.             //
  103.             //    Simply hide the region given to us by inverting it.
  104.             //
  105.  
  106.             InvertRgn(hideRgn);
  107.             result = noErr;
  108.             break;
  109.     }
  110.  
  111.     return(result);
  112. }
  113.  
  114. #endif USE_CUSTOM_DRAWING
  115.  
  116.  
  117. /*
  118.  *    Given a point in global coordinates, HitTest returns a pointer to a
  119.  *    document structure if the point is inside a document window on the screen.
  120.  *    If the point is not inside a document window, HitTest return NULL in
  121.  *    theDoc. If the point is in a doument window and also in the viewRect of
  122.  *    the document's TextEdit field, HitTest also returns the offset into
  123.  *    the text that corresponds to that point. If the point is not in the text,
  124.  *    HitTest returns -1.
  125.  */
  126.  
  127. short HitTest(Point theLoc, Document **theDoc)
  128.  
  129. {    WindowPtr        theWindow;
  130.     short            offset;
  131.  
  132.     *theDoc = 0L;
  133.     offset = -1;
  134.  
  135.     if (FindWindow(theLoc, &theWindow) == inContent) {
  136.         if (*theDoc = IsDocumentWindow(theWindow)) {
  137.  
  138.             SetPort(theWindow);
  139.             GlobalToLocal(&theLoc);
  140.  
  141.             if ((PtInRect(theLoc, &(**((**theDoc).theTE)).viewRect)) && 
  142.                 (PtInRect(theLoc, &(**((**theDoc).theTE)).destRect))) {
  143.  
  144.                 offset = TEGetOffset(theLoc, (**theDoc).theTE);
  145.  
  146.                 if ((TEIsFrontOfLine(offset, (**theDoc).theTE)) && (offset) &&            
  147.                         ((*((**((**theDoc).theTE)).hText))[offset - 1] != 0x0D) &&
  148.                         (TEGetPoint(offset - 1, (**theDoc).theTE).h < theLoc.h)) {
  149.                     offset--;
  150.                 }
  151.             }
  152.         }
  153.     }
  154.  
  155.     return(offset);
  156. }
  157.  
  158.  
  159. /*
  160.  *    DrawCaret draws a caret in a TextEdit field at the given offset. DrawCaret
  161.  *    expects the port to be set to the port that the TextEdit field is in.
  162.  *    DrawCaret inverts the image of the caret onto the screen.
  163.  */
  164.  
  165. short DrawCaret(short offset, TEHandle theTE)
  166.  
  167. {    Point        theLoc;
  168.     short        theLine, lineHeight;
  169.  
  170.     /*
  171.      *    Get the coordinates and the line of the offset to draw the caret.
  172.      */
  173.  
  174.     theLoc  = TEGetPoint(offset, theTE);
  175.     theLine = TEGetLine(offset, theTE);
  176.  
  177.     /*
  178.      *    For some reason, TextEdit dosen't return the proper coordinates
  179.      *    of the last offset in the field if the last character in the record
  180.      *    is a carriage return. TEGetPoint returns a point that is one line
  181.      *    higher than expected. The following code fixes this problem.
  182.      */
  183.  
  184.     if ((offset == (**theTE).teLength) &&
  185.             (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D) {
  186.         theLoc.v += TEGetHeight(theLine, theLine, theTE);
  187.     }
  188.  
  189.     /*
  190.      *    Always invert the caret when drawing.
  191.      */
  192.  
  193.     PenMode(patXor);
  194.  
  195.     /*
  196.      *    Get the height of the line that the offset points to.
  197.      */
  198.  
  199.     lineHeight = TEGetHeight(theLine, theLine, theTE);
  200.  
  201.     /*
  202.      *    Draw the appropriate caret image.
  203.      */
  204.  
  205.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  206.     Line(0, 1 - lineHeight);
  207.  
  208.     PenNormal();
  209. }
  210.  
  211.  
  212. char GetCharAtOffset(short offset, TEHandle theTE)
  213.  
  214. {
  215.     if (offset < 0)
  216.         return(0x0D);
  217.  
  218.     return(((char *) *((**theTE).hText))[offset]);
  219. }
  220.  
  221.  
  222. Boolean WhiteSpace(char theChar)
  223.  
  224. {
  225.     return((theChar == ' ') || (theChar == 0x0D));
  226. }
  227.  
  228.  
  229. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  230.  
  231. {    char        theChar;
  232.  
  233.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  234.         return(true);
  235.  
  236.     theChar = ((char *) *((**theTE).hText))[offset];
  237.     return((theChar == ' ') || (theChar == 0x0D));
  238. }
  239.  
  240.  
  241. void InsertTextAtOffset(short offset, char *theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  242.  
  243. {
  244.     if (size == 0)
  245.         return;
  246.  
  247.     //
  248.     //    If inserting at the end of a word and the selection does not begin with
  249.     //    a space, insert a space before the insertion.
  250.     //
  251.  
  252.     if (!WhiteSpaceAtOffset(offset - 1, theTE) &&
  253.          WhiteSpaceAtOffset(offset, theTE) &&
  254.         !WhiteSpace(theBuf[0])) {
  255.  
  256.         TESetSelect(offset, offset, theTE);
  257.         TEKey(' ', theTE);
  258.         offset++;
  259.     }
  260.  
  261.     //
  262.     //    If inserting at the beginning of a word and the selection does not end
  263.     //    with a space, insert a space after the insertion.
  264.     //
  265.  
  266.     if ( WhiteSpaceAtOffset(offset - 1, theTE) &&
  267.         !WhiteSpaceAtOffset(offset, theTE) &&
  268.         !WhiteSpace(theBuf[size - 1])) {
  269.  
  270.         TESetSelect(offset, offset, theTE);
  271.         TEKey(' ', theTE);
  272.     }
  273.  
  274.     TESetSelect(offset, offset, theTE);
  275.     TEStylInsert(theBuf, size, theStyl, theTE);
  276.     TESetSelect(offset, offset + size, theTE);
  277. }
  278.  
  279.  
  280. short GetSelectionSize(Document *theDocument)
  281.  
  282. {
  283.     return((**(theDocument->theTE)).selEnd - (**(theDocument->theTE)).selStart);
  284. }
  285.  
  286.  
  287. Ptr GetSelectedTextPtr(Document *theDocument)
  288.  
  289. {
  290.     return((*(**(theDocument->theTE)).hText) + (**(theDocument->theTE)).selStart);
  291. }
  292.  
  293.  
  294. /*
  295.  *    MySendDataProc
  296.  *
  297.  *    Will provide 'styl' data for the drag when requested.
  298.  */
  299.  
  300. pascal OSErr MySendDataProc(FlavorType theType, void *refCon,
  301.                             ItemReference theItem, DragReference theDrag)
  302.  
  303. {    Document        *theDocument = (Document *) refCon;
  304.     StScrpHandle    theStyl;
  305.  
  306.     if (theType == 'styl') {
  307.  
  308.         theStyl = GetStylScrap(theDocument->theTE);
  309.  
  310.         //
  311.         //    Call SetDragItemFlavorData to provide the requested data.
  312.         //
  313.  
  314.         HLock((Handle) theStyl);
  315.         SetDragItemFlavorData(theDrag, theItem, 'styl', (Ptr) *theStyl,
  316.                               GetHandleSize((Handle) theStyl), 0L);
  317.         HUnlock((Handle) theStyl);
  318.         DisposeHandle((Handle) theStyl);
  319.  
  320.     } else {
  321.  
  322.         return(badDragFlavorErr);
  323.  
  324.     }
  325.  
  326.     return(noErr);
  327. }
  328.  
  329.  
  330. /*
  331.  *    MyReceiveDropHandler
  332.  *
  333.  *    Called by the Drag Manager when a drop occurs over one of the DragText document windows.
  334.  */
  335.  
  336. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon,
  337.                                   DragReference theDrag)
  338.  
  339. {    OSErr                result;
  340.     TEHandle            tempTE;
  341.     Rect                theRect, srcRect;
  342.     unsigned short        items, index;
  343.     ItemReference        theItem;
  344.     DragAttributes        attributes;
  345.     Ptr                    textData;
  346.     StScrpHandle        stylHandle;
  347.     Size                textSize, stylSize;
  348.     short                offset, selStart, selEnd, mouseDownModifiers, mouseUpModifiers, moveText;
  349.     Document            *theDocument = (Document *) handlerRefCon;
  350.     WindowOffscreen        *theOffscreen;
  351.     Point                thePoint;
  352.  
  353.     if ((!canAcceptItems) || (insertPosition == -1))
  354.         return(dragNotAcceptedErr);
  355.  
  356.     SetPort(theWindow);
  357.  
  358.     GetDragAttributes(theDrag, &attributes);
  359.     GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
  360.  
  361.     moveText = (attributes & dragInsideSenderWindow) &&
  362.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  363.  
  364.     //
  365.     //    Loop through all of the drag items contained in this drag and collect the text
  366.     //    into the tempTE record.
  367.     //
  368.  
  369.     SetRect(&theRect, 0, 0, 0, 0);
  370.     tempTE = TEStylNew(&theRect, &theRect);
  371.  
  372.     CountDragItems(theDrag, &items);
  373.  
  374.     for (index = 1; index <= items; index++) {
  375.  
  376.         //
  377.         //    Get the item's reference number, so we can refer to it.
  378.         //
  379.  
  380.         GetDragItemReferenceNumber(theDrag, index, &theItem);
  381.  
  382.         //
  383.         //    Try to get the flags for a 'TEXT' flavor. If this returns noErr,
  384.         //    then we know that a 'TEXT' flavor exists in the item.
  385.         //
  386.  
  387.         result = GetFlavorDataSize(theDrag, theItem, 'TEXT', &textSize);
  388.  
  389.         if (result == noErr) {
  390.  
  391.             textData = NewPtr(textSize);
  392.             if (textData == 0L) {
  393.                 TEDispose(tempTE);
  394.                 return(memFullErr);
  395.             }
  396.  
  397.             GetFlavorData(theDrag, theItem, 'TEXT', textData, &textSize, 0L);
  398.  
  399.             //
  400.             //    Check for optional styl data for the TEXT.
  401.             //
  402.  
  403.             stylHandle = 0L;
  404.             result = GetFlavorDataSize(theDrag, theItem, 'styl', &stylSize);
  405.             if (result == noErr) {
  406.  
  407.                 stylHandle = (StScrpHandle) NewHandle(stylSize);
  408.                 if (stylHandle == 0L) {
  409.                     TEDispose(tempTE);
  410.                     DisposePtr(textData);
  411.                     return(memFullErr);
  412.                 }
  413.  
  414.                 HLock((Handle) stylHandle);
  415.                 GetFlavorData(theDrag, theItem, 'styl', *stylHandle, &stylSize, 0L);
  416.                 HUnlock((Handle) stylHandle);
  417.  
  418.             }
  419.  
  420.             //
  421.             //    Insert this drag item's text into the tempTE.
  422.             //
  423.  
  424.             TESetSelect(32767, 32767, tempTE);
  425.             TEStylInsert(textData, textSize, stylHandle, tempTE);
  426.  
  427.             DisposePtr(textData);
  428.             if (stylHandle)
  429.                 DisposeHandle((Handle) stylHandle);
  430.         }
  431.     }
  432.  
  433.     //
  434.     //    Pull the TEXT and styl data out of the tempTE handle.
  435.     //
  436.  
  437.     textData = NewPtr(textSize = (**tempTE).teLength);
  438.     if (textData == 0L) {
  439.         TEDispose(tempTE);
  440.         return(memFullErr);
  441.     }
  442.     BlockMove(*(**tempTE).hText, textData, textSize);
  443.  
  444.     TESetSelect(0, 32767, tempTE);
  445.     stylHandle = GetStylScrap(tempTE);
  446.  
  447.     TEDispose(tempTE);
  448.  
  449.     //
  450.     //    If we actually received text, insert it into the destination.
  451.     //
  452.  
  453.     if (textSize != 0) {
  454.  
  455.         //
  456.         //    If the caret or highlighting is on the screen, remove it/them.
  457.         //
  458.  
  459.         offset = caretOffset;
  460.  
  461.         if (caretOffset != -1) {
  462.             DrawCaret(caretOffset, theDocument->theTE);
  463.             caretOffset = -1;
  464.         }
  465.  
  466.         if (attributes & dragHasLeftSenderWindow) {
  467.             HideDragHilite(theDrag);
  468.         }
  469.  
  470.         //
  471.         //    If the drag occurred completely within the same window and the window is not
  472.         //    frontmost, bring the window forward and update its contents before completing
  473.         //    the drag.
  474.         //
  475.  
  476.         if ((attributes & dragInsideSenderWindow) && (theDocument->theWindow != FrontWindow())) {
  477.             SelectWindow(theDocument->theWindow);
  478.             UpdateWindow(theDocument);
  479.             TEActivate(theDocument->theTE);
  480.         }
  481.  
  482.         //
  483.         //    Draw everything into offscreen pixmap.
  484.         //
  485.  
  486.         if (theOffscreen = DrawOffscreen(theDocument->theWindow)) {
  487.             (**(theDocument->theTE)).inPort = (GrafPtr) theOffscreen->offscreenWorld;
  488.         }
  489.  
  490.         //
  491.         //    If the window is not active, must activate TE before inserting
  492.         //    text or the background hilite will not update correctly.
  493.         //
  494.  
  495.         if (!((WindowPeek) theDocument->theWindow)->hilited) {
  496.             TEActivate(theDocument->theTE);
  497.         }
  498.  
  499.         //
  500.         //    If this window is also the sender, delete source selection if no
  501.         //    option key.
  502.         //
  503.  
  504.         if (moveText) {
  505.             selStart = (**(theDocument->theTE)).selStart;
  506.             selEnd   = (**(theDocument->theTE)).selEnd;
  507.             if ( WhiteSpaceAtOffset(selStart - 1, theDocument->theTE) &&
  508.                 !WhiteSpaceAtOffset(selStart, theDocument->theTE) &&
  509.                 !WhiteSpaceAtOffset(selEnd - 1, theDocument->theTE) &&
  510.                  WhiteSpaceAtOffset(selEnd, theDocument->theTE)) {
  511.                  
  512.                  if (GetCharAtOffset(selEnd, theDocument->theTE) == ' ')
  513.                     (**(theDocument->theTE)).selEnd++;
  514.             }
  515.             if (insertPosition > selStart) {
  516.                 insertPosition -= ((**(theDocument->theTE)).selEnd -
  517.                                    (**(theDocument->theTE)).selStart);
  518.             }
  519.             srcRect = (**theDocument->hiliteRgn).rgnBBox;
  520.             TEDelete(theDocument->theTE);
  521.         }
  522.  
  523.         InsertTextAtOffset(insertPosition, textData, textSize, stylHandle, theDocument->theTE);
  524.  
  525.         TEGetHiliteRgn(theDocument->hiliteRgn, theDocument->theTE);
  526.  
  527.         //
  528.         //    If the text is moving (not copying) within the same window, provide a ZoomRects
  529.         //    from the source to the destination before revealing the reflowed text.
  530.         //
  531.  
  532.         if (moveText) {
  533.             theRect = (**theDocument->hiliteRgn).rgnBBox;
  534.             thePoint.h = thePoint.v = 0;
  535.             SetPort(theWindow);
  536.             LocalToGlobal(&thePoint);
  537.             OffsetRect(&srcRect, thePoint.h, thePoint.v);
  538.             OffsetRect(&theRect, thePoint.h, thePoint.v);
  539.             ZoomRects(&srcRect, &theRect, 12, zoomDecelerate);
  540.         }
  541.  
  542.         theDocument->dirty = true;
  543.     }
  544.  
  545.     DisposePtr(textData);
  546.  
  547.     if (stylHandle)
  548.         DisposeHandle((Handle) stylHandle);
  549.  
  550.     //
  551.     //    Undo the TEActivate, if needed.
  552.     //
  553.  
  554.     if (!((WindowPeek) theDocument->theWindow)->hilited) {
  555.         TEDeactivate(theDocument->theTE);
  556.     }
  557.  
  558.     //
  559.     //    Show the offscreen bitmap.
  560.     //
  561.  
  562.     DrawOnscreen(theOffscreen);
  563.     (**(theDocument->theTE)).inPort = theDocument->theWindow;
  564.  
  565.     return(noErr);
  566. }
  567.  
  568.  
  569. /*
  570.  *    MyTrackingHandler
  571.  *
  572.  *    This is the drag tracking handler for windows in the DragText application.
  573.  */
  574.  
  575. pascal OSErr MyTrackingHandler(short message, WindowPtr theWindow,
  576.                                void *handlerRefCon, DragReference theDrag)
  577.  
  578. {    short                result, offset;
  579.     long                theTime = TickCount();
  580.     unsigned short        count, index;
  581.     unsigned long        flavorFlags, attributes;
  582.     ItemReference        theItem;
  583.     RgnHandle            theRgn;
  584.     Document            *theDocument = (Document *) handlerRefCon;
  585.     Document            *hitDoc;
  586.     Point                theMouse, localMouse;
  587.  
  588.     if ((message != dragTrackingEnterHandler) && (!canAcceptItems))
  589.         return(noErr);
  590.  
  591.     GetDragAttributes(theDrag, &attributes);
  592.  
  593.     switch (message) {
  594.  
  595.         case dragTrackingEnterHandler:
  596.  
  597.             //
  598.             //    We get called with this message the first time that a drag enters ANY
  599.             //    window in our application. Check to see if all of the drag items contain
  600.             //    TEXT. We only accept a drag if all of the items in the drag can be accepted.
  601.             //
  602.  
  603.             canAcceptItems = true;
  604.  
  605.             CountDragItems(theDrag, &count);
  606.  
  607.             for (index = 1; index <= count; index++) {
  608.                 GetDragItemReferenceNumber(theDrag, index, &theItem);
  609.  
  610.                 result = GetFlavorFlags(theDrag, theItem, 'TEXT', &flavorFlags);
  611.  
  612.                 if (result != noErr) {
  613.                     canAcceptItems = false;
  614.                     break;
  615.                 }
  616.             }
  617.  
  618.             break;
  619.  
  620.         case dragTrackingEnterWindow:
  621.  
  622.             //
  623.             //    We receive an EnterWindow message each time a drag enters one of our
  624.             //    application's windows. We initialize our global variables for tracking
  625.             //    the drag through the window.
  626.             //
  627.  
  628.             caretTime = theTime;
  629.             caretOffset = lastOffset = -1;
  630.             caretShow = true;
  631.  
  632.             cursorInContent = false;
  633.  
  634.             break;
  635.  
  636.         case dragTrackingInWindow:
  637.  
  638.             //
  639.             //    We receive InWindow messages as long as the mouse is in one of our windows
  640.             //    during a drag. We draw the window highlighting and blink the insertion caret
  641.             //    when we get these messages.
  642.             //
  643.  
  644.             GetDragMouse(theDrag, &theMouse, 0L);
  645.             localMouse = theMouse;
  646.             GlobalToLocal(&localMouse);
  647.  
  648.             //
  649.             //    Show or hide the window highlighting when the mouse enters or leaves the
  650.             //    TextEdit field in our window (we don't want to show the highlighting when
  651.             //    the mouse is over the window title bar or over the scroll bars).
  652.             //
  653.  
  654.             if (attributes & dragHasLeftSenderWindow) {
  655.                 if (PtInRect(localMouse, &(**(theDocument->theTE)).viewRect)) {
  656.  
  657.                     if (!cursorInContent) {
  658.                         RectRgn(theRgn = NewRgn(), &(**(theDocument->theTE)).viewRect);
  659.                         ShowDragHilite(theDrag, theRgn, true);
  660.                         DisposeRgn(theRgn);
  661.                     }
  662.                     cursorInContent = true;
  663.  
  664.                 } else {
  665.  
  666.                     if (cursorInContent) {
  667.                         HideDragHilite(theDrag);
  668.                     }
  669.                     cursorInContent = false;
  670.  
  671.                 }
  672.             }
  673.  
  674.             offset = HitTest(theMouse, &hitDoc);
  675.  
  676.             //
  677.             //    If this application is the sender, do not allow tracking through
  678.             //    the selection in the window that sourced the drag.
  679.             //
  680.  
  681.             if (attributes & dragInsideSenderWindow) {
  682.                 if ((offset >= (**(theDocument->theTE)).selStart) &&
  683.                     (offset <= (**(theDocument->theTE)).selEnd)) {
  684.                         offset = -1;
  685.                 }
  686.             }
  687.  
  688.             if (hitDoc == theDocument) {
  689.  
  690.                 insertPosition = offset;
  691.  
  692.                 //
  693.                 //    Reset flashing counter if the offset has moved. This makes the
  694.                 //    caret blink only after the caret has stopped moving long enough.
  695.                 //
  696.  
  697.                 if (offset != lastOffset) {
  698.                     caretTime = theTime;
  699.                     caretShow = true;
  700.                 }
  701.                 lastOffset = offset;
  702.  
  703.                 //
  704.                 //    Flash caret.
  705.                 //
  706.  
  707.                 if (theTime - caretTime > gCaretTime) {
  708.                     caretShow = !caretShow;
  709.                     caretTime = theTime;
  710.                 }
  711.                 if (! caretShow) {
  712.                     offset = -1;
  713.                 }
  714.  
  715.                 //
  716.                 //    If caret offset has changed, move caret on screen.
  717.                 //
  718.  
  719.                 if (offset != caretOffset) {
  720.                     if (caretOffset != -1) {
  721.                         DrawCaret(caretOffset, theDocument->theTE);
  722.                     }
  723.                     if (offset != -1) {
  724.                         DrawCaret(offset, theDocument->theTE);
  725.                     }
  726.                 }
  727.  
  728.                 caretOffset = offset;
  729.  
  730.             } else {
  731.  
  732.                 lastOffset = offset;
  733.                 insertPosition = -1;
  734.  
  735.             }
  736.  
  737.             break;
  738.  
  739.         case dragTrackingLeaveWindow:
  740.  
  741.             //
  742.             //    If the caret is on the screen, remove it.
  743.             //
  744.  
  745.             if (caretOffset != -1) {
  746.                 DrawCaret(caretOffset, theDocument->theTE);
  747.                 caretOffset = -1;
  748.             }
  749.  
  750.             //
  751.             //    Remove window highlighting, if showing.
  752.             //
  753.  
  754.             if ((cursorInContent) && (attributes & dragHasLeftSenderWindow))
  755.                 HideDragHilite(theDrag);
  756.  
  757.             break;
  758.  
  759.         case dragTrackingLeaveHandler:
  760.             break;
  761.  
  762.     }
  763.  
  764.     return(noErr);
  765. }
  766.  
  767.  
  768. /*
  769. **    DropLocationIsFinderTrash returns true if the given
  770. **    dropLocation AEDesc is a descriptor of the Finder's Trash.
  771. */
  772.  
  773. Boolean DropLocationIsFinderTrash(AEDesc *dropLocation)
  774.  
  775. {
  776.     Boolean            result;
  777.     AEDesc            dropSpec;
  778.     FSSpec            *theSpec;
  779.     CInfoPBRec        pb;
  780.     short            trashVRefNum;
  781.     long            trashDirID;
  782.     
  783.     result = false;    /* default to false */
  784.     
  785.     /* Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or */
  786.     /* it can't be coerced into an FSSpec, then it couldn't have been the Trash. */    
  787.     if ( (dropLocation->descriptorType != typeNull) &&
  788.          (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr) )
  789.     {
  790.         /* Lock dropSpec.dataHandle while we use it as a pointer */ 
  791.         HLock(dropSpec.dataHandle);
  792.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  793.  
  794.         /* Get the directory ID of the given dropLocation object. */
  795.         pb.dirInfo.ioNamePtr = theSpec->name;
  796.         pb.dirInfo.ioVRefNum = theSpec->vRefNum;
  797.         pb.dirInfo.ioFDirIndex = 0;
  798.         pb.dirInfo.ioDrDirID = theSpec->parID;
  799.         if ( PBGetCatInfoSync(&pb) == noErr )
  800.         {
  801.             /* The drop location must be a directory to be the trash */
  802.             if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  803.             {
  804.                 /* Find the trash folder on the drop location's volume. */ 
  805.                 if ( FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder,
  806.                                 &trashVRefNum, &trashDirID) == noErr )
  807.                 {
  808.                     /* If the directory ID of the dropLocation object is the same as the */
  809.                     /* directory ID returned by FindFolder, then the drop must have */
  810.                     /* occurred into the Trash. */
  811.                     result = ( pb.dirInfo.ioDrDirID == trashDirID );
  812.                 }
  813.             }
  814.         }
  815.         /* Unlock and dispose of the descriptor record */
  816.         HUnlock(dropSpec.dataHandle);
  817.         AEDisposeDesc(&dropSpec);
  818.     }
  819.     
  820.     return ( result );
  821. }
  822.  
  823.  
  824. /*
  825.  *    DragText
  826.  *
  827.  *    Drag the selected text in the given document.
  828.  */
  829.  
  830. short DragText(Document *theDocument, EventRecord *theEvent, RgnHandle hiliteRgn)
  831.  
  832. {    short                result;
  833.     RgnHandle            dragRegion, tempRgn;
  834.     Point                theLoc;
  835.     DragReference        theDrag;
  836.     StScrpHandle        theStyl;
  837.     AEDesc                dropLocation;
  838.     DragAttributes        attributes;
  839.     short                mouseDownModifiers, mouseUpModifiers, copyText;
  840.  
  841.     //
  842.     //    Copy the hilite region into dragRegion and offset it into global coordinates.
  843.     //
  844.  
  845.     CopyRgn(hiliteRgn, dragRegion = NewRgn());
  846.     SetPt(&theLoc, 0, 0);
  847.     LocalToGlobal(&theLoc);
  848.     OffsetRgn(dragRegion, theLoc.h, theLoc.v);
  849.  
  850.     //
  851.     //    Wait for the mouse to move to the mouse button to be released. If the mouse button was
  852.     //    released before the mouse moves, return false. Returing false from DragText means that
  853.     //    a drag operation did not occur.
  854.     //
  855.  
  856.     if (! WaitMouseMoved(theEvent->where)) {
  857.         return(false);
  858.     }
  859.  
  860.     NewDrag(&theDrag);
  861.  
  862.     //
  863.     //    For purposes of demonstration, we insert the 'TEXT' data and promise 'styl'
  864.     //    data. If a receiver requests 'TEXT', the Drag Manager will give them the text
  865.     //    without needing us to intervene. If a receiver requests 'styl', the Drag Manager
  866.     //    will call our MySendDataProc to provide the data at drop time. The MySendDataProc
  867.     //    is specified by calling SetDragSendProc.
  868.     //
  869.  
  870.     AddDragItemFlavor(theDrag, 1, 'TEXT', GetSelectedTextPtr(theDocument),
  871.                       GetSelectionSize(theDocument), 0);
  872.  
  873.     theStyl = GetStylScrap(theDocument->theTE);
  874.     HLock((Handle) theStyl);
  875.     AddDragItemFlavor(theDrag, 1, 'styl', (Ptr) *theStyl, GetHandleSize((Handle) theStyl), 0);
  876.     HUnlock((Handle) theStyl);
  877.     DisposeHandle((Handle) theStyl);
  878.  
  879.     SetDragSendProc(theDrag, MySendDataProc, (void *) theDocument);
  880.  
  881.     //
  882.     //    Set the item's bounding rectangle in global coordinates.
  883.     //
  884.  
  885.     SetDragItemBounds(theDrag, 1, &(**dragRegion).rgnBBox);
  886.  
  887.     //
  888.     //    Prepare the drag region.
  889.     //
  890.  
  891.     tempRgn = NewRgn();
  892.     CopyRgn(dragRegion, tempRgn);
  893.     InsetRgn(tempRgn, 1, 1);
  894.     DiffRgn(dragRegion, tempRgn, dragRegion);
  895.     DisposeRgn(tempRgn);
  896.  
  897. #ifdef USE_CUSTOM_DRAWING
  898.  
  899.     SetDragDrawingProc(theDrag, MyDrawingProc, 0L);
  900.  
  901. #endif
  902.  
  903.     //
  904.     //    Drag the text. TrackDrag will return userCanceledErr if the drop zoomed-back
  905.     //    for any reason.
  906.     //
  907.  
  908.     result = TrackDrag(theDrag, theEvent, dragRegion);
  909.  
  910.     if (result != noErr && result != userCanceledErr) {
  911.         return(true);
  912.     }
  913.  
  914.     //
  915.     //    Check to see if the drop occurred in the Finder's Trash. If the drop occurred
  916.     //    in the Finder's Trash and a copy operation wasn't specified, delete the
  917.     //    source selection. Note that we can continute to get the attributes, drop location
  918.     //    modifiers, etc. of the drag until we dispose of it using DisposeDrag.
  919.     //
  920.  
  921.     GetDragAttributes(theDrag, &attributes);
  922.     if (!(attributes & dragInsideSenderApplication)) {
  923.  
  924.         GetDropLocation(theDrag, &dropLocation);
  925.  
  926.         GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
  927.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  928.  
  929.         if ((!copyText) && (DropLocationIsFinderTrash(&dropLocation))) {
  930.             TEDelete(theDocument->theTE);
  931.             theDocument->dirty = true;
  932.         }
  933.  
  934.         AEDisposeDesc(&dropLocation);
  935.     }
  936.  
  937.     //
  938.     //    Dispose of the drag.
  939.     //
  940.  
  941.     DisposeDrag(theDrag);
  942.  
  943.     DisposeRgn(dragRegion);
  944.  
  945.     return(true);
  946. }
  947.  
  948.  
  949.  
  950.  
  951.